HTTP/3 初体验
HTTP协议经过发展,目前HTTP2.0作为主流HTTP协议,已经得到一定普及,虽然国内仍然有很多连HTTPS都没上的网站,但不影响HTTP协议的发展。
HTTP2.0我们知道相较于HTTP1.1版本的协议,进行了很多的优化,可以参考之前公众号的一篇文章《如何优化你的HTTPS》,里面有关于HTTP2.0的优化方面的所有介绍,现在HTTP-over-QUIC出了也有一段时间了,虽然还是实验性协议,但是IETF HTTP和QUIC工作组主席Mark Nottingham正式提出将HTTP-over-QUIC重命名为HTTP/3.0到现在已经有一年多的时间了,所以HTTP-over-QUIC成为HTTP/3.0算是没跑了,所以还是早点认识一下这个新版本的协议
QUIC全名是(Quick UDP Internet Connections),它是google在2013年开发实现的
我们都知道,TCP和UDP最本质的区别在于可靠传输,HTTP2.0及之前的协议建立在TCP上,TCP为了达到可靠传输,必须要有确认机制,需要来回握手确认来建立链接,即便HTTP2.0中做了很多优化,但仍然摆脱不了TCP的三次握手,而且TCP协议在处理包时是有严格顺序的,当其中一个数据包遇到问题时,TCP链接需要等待整个包重传之后才能继续进行,虽然HTTP2.0中通过多个stream,使得逻辑上一个TCP链接上的并行内容,进行多路数据传输,然而这中间没有关联的数据,当stream2的帧没有收到,后面stream1的帧也会因此阻塞
所以google在QUIC协议中基于UDP协议,跳出TCP协议,它是在两个端点之间创建链接,且支持多路复用,并且在设计之初就考虑希望能够提供等同于SSL/TLS层级的安全保障的同时,减少数据传输及创建链接时的延迟时间,双向控制带宽,从而达到更快速的体验,先通过一个动图直观对比下
接着看下QUIC有什么优势,已经通过什么方法解决TCP的一些限制及问题
新定义连接机制
在TCP连接中,一条TCP连接是由四元组标识的,分别是源IP、源端口、目的IP、目的端口,一旦一个元素发生变化时,就会断开重连,重新进行三次握手,导致一定的延时
在基于UDP的QUIC中,是在自己的逻辑里面维护连接的机制,不再是以四元组标识,而是以一个64位的随机数作为ID来标识,而且UDP是无连接的,所以当IP或端口变化的时候,只要ID不变,就不需要重新建立连接
新定义重传机制
在TCP连接中,为了保证可靠性,通过使用序号和应答机制,来解决顺序问题和丢包问题,如上面说到的,即便HTTP2.0使用stream的方式,也还是存在阻塞的问题
在TCP中,任何一个序号的包发过去,都要在一定时间内得到应答,超时之后,就会触发重传,重新发送这个序号的包,而RTO(重传超时时间)的计算相对复杂,现在都是通过自适应算法设定RTO的值,而这个计算的不准确会直接导致网络的吞吐量和网络资源利用率
在QUIC中,也有个序列号PN(Packet Number),是递增的,任何一个序列号的包,只发送一次,下次就要加1,根据Packet Number值就可以计算出是重传响应还是原始响应,这样可以准确计算RTT时间
但是有一个问题,就是UDP无连接,所以没法确认两个包是否是同样的内容,也就是没有办法确定发送出去的包是不是重传包,QUIC虽然基于UDP,但它也是一个可靠数据传输协议,所以QUIC又定义了一个offset概念,在发送的数据流里面有个偏移量offset,可以通过offset查看数据发送到了哪里,这样只有这个offset的包没有来,就要重发,如果来了,按照offset拼接成一个完整的数据流
对于重传,QUIC有个特性就是关键包短时间内发送多次,这样以确保重要的节点不被Delay
没有HOL的多路复用
QUIC的多路复用和HTTP2类似,在一条QUIC连接上可以并发发送多个HTTP请求,但是QUIC的多路复用比HTTP2有一个很大的优势,那就是QUIC一个连接上的多个stream之间没有依赖,这样,假如stream2丢了一个udp packet,也只会影响stream2的处理,不会影响stream2之前以及之后的stream的处理,这就很大程度上缓解了HOL阻塞的问题
当然并不是所有的QUIC数据丢失都不会受到HOL阻塞影响,比如QUIC使用Hpack压缩算法的时候,由于算法的限制,丢失一个头部数据时,可能遇到HOL阻塞
流量控制
UDP没有流量控制机制,由于QUIC的多路复用机制,其流量控制分为Stream和Connection两种级别的控制
Stream就可以理解为一条HTTP请求
Connection可以理解为一条TCP连接
具体的实现机制是
通过window_update帧告诉对端自己可以接收的字节数,这样发送方就不会发送超过这个数量的数据
通过BlockFrame告诉对端由于流量控制被阻塞了,无法发送数据
QUIC的流量控制和TCP的有点区别,TCP是使用了滑动窗口机制来进行流量控制,为了保证可靠性,窗口左边沿向右滑动的长度取决于已经确认的字节数,如果中间出现丢包,就算接收到了更大序列号的Segment,窗口也无法超过这个序列号,但是QUIC不同,就算此前有些Packet没有接收到,它的滑动只取决于接收到的最大偏移字节数
对于Connection级别的流量窗口,其接收窗口大小就是各个stream接收窗口大小之和
而在Connection中,不同的stream互相独立,不会引起HOL阻塞
所以在弱网环境下,QUIC优势更明显
HTTP/3.0综上所述,有这么多优点,必须要体验一下,下面介绍下Nginx支持HTTP/3.0
Nginx原生不支持HTTP/3.0,这里需要借助Cloudflare提供的一个补丁来让Nginx支持HTTP/3.0
这里因为要补丁编译安装,所以需要下载nginx源码包,我这里仍然用nginx1.17.7版本来测试,下载包解压包就不说了
接着通过git下载QUIC补丁
然后因为要用patch打补丁,所有通过yum安装patch
接着就开始打补丁
这里只有nginx1.16的补丁,我也不确定,看着是打上了,编译试一下
重要的是with-http_v3_module、openssl用quiche/deps下面的boringssl,另外就是--with-quiche指向quiche目录
接着make,这里因为没有cmake报了个错误,通过yum安装cmake3,
注意要用cmake3.0以上版本,所以用yum install cmake3,这个要开启epel源
然后重新make,这里还需要安装gcc-c++,cargo、golang环境,否则在编译boringssl的时候会报错,编译不过去
接着是漫长的等待,如果服务器性能好的话,可以用make -j加速编译
在后面使用cargo编译的时候,因为默认是creates.io的仓库,实在太慢了,所以这里建议更换为中科大的源,在/root/.cargo下面新建config文件,内容如下:
更多cargo的可以查看cargo中文社区
另外就是编译boringssl的时候,编译出的库为静态库,最后ld链接的时候无法连接,需要在nginx编译之后生成的objs/Makefile中修改cmake编译参数,设置为-fPIC,生成共享库,否则编译失败
之后即可编译成功
在objs下面将nginx二进制文件覆盖掉原来的nginx,开始配置nginx配置文件
在quiche中通过cargo构建客户端,通过http3-client测试请求
客户端请求
请求日志查看
抓包查看
如果是chrome浏览器开启QUIC
需要重启chrome才能生效
当然如果不想像我一样折腾,直接用docker就可以体验了
docker run -it -p 443:443 -p 443:443/udp \ -v $PWD/nginx.conf:/usr/local/nginx/conf/nginx.conf \ -v /root/cert/haid.com.cn.pem:/etc/ssl/certs/server.crt \ -v /root/cert/haid.com.cn.key:/etc/ssl/private/server.key \ nwtgck/nginx-http3
折腾环境太费劲了,后面有机会做quic和http2的对比,以及分析 quic包,折腾不易,欢迎转发、在看!
更多精彩内容请扫描下方二维码关注公众号